<?php
/**
 * Pert PHP Library
 * MySQL Classes
 *
 * @author	Emre Hasegeli <hasegeli@gmail.com>
 * @license	GNU General Public License v3
 */

namespace Pert;

class Mysql_connection
{
	private $name;

	private $host;

	private $port;

	private $user;

	private $password;

	const persistentConnection = true;

	protected function __Construct($name,
			$host,
			$port,
			$user,
			$password)
	{
		$this->name= $name;

		$this->host= $host;

		$this->port= $port;

		$this->user= $user;

		$this->password= $password;
	}

	public function __ToString()
	{
		return $this->name;
	}

	private $connection;

	protected function selectConnection()
	{
		if(isset($this->connection))
			return $this->connection;

		if(self::persistentConnection)
			if($this->connection = mysql_pconnect($this->host . ':' . $this->port,
					$this->user,
					$this->password))
				if(mysql_select_db($this->name,
						$this->connection))
					return $this->connection;

		if($this->connection = mysql_connect($this->host . ':' . $this->port,
				$this->user,
				$this->password))
			if(mysql_select_db($this->name,
					$this->connection))
				return $this->connection;

		throw new Mysql_exception($this,
				'Cannot connected to the database.');

		return $this->connection;
	}

	protected function __Destruct()
	{
		if(!self::persistentConnection)
			if(isset($this->connection))
				mysql_close($this->connection);
	}
}

class MySQL extends Mysql_connection
{
	private $totalQueryCount;

	private $totalQueryTime;

	private $connectionTime;

	public static $Debug = false;

	public function __Construct($name,
			$host = 'localhost',
			$post = '5432',
			$user = 'root',
			$password = NULL)
	{
		parent::__Construct($name,
				$host,
				$post,
				$user,
				$password);

		if(self::$Debug)
		{
			$this->totalQueryCount = 0;

			$this->totalQueryTime = 0;
		}
	}

	private function totalQueryTimeString()
	{
		if($this->totalQueryCount)
			return $this->totalQueryCount . ' Query executed in ' . $this->totalQueryTime . ' second on "' . $this . '" database.';

		return '';
	}

	public function __Destruct()
	{
		parent::__Destruct();

		if(self::$Debug)
			echo $this->totalQueryTimeString();
	}

	private $queryArray = array();

	private function checkQueryIfExecuted($query)
	{
		foreach($this->queryArray AS $q)
			if($q == $query)
				echo "<p >Same query: '" . $q . "'.</p>";

		$this->queryArray[] = $query;
	}

	private function startTime()
	{
		$microTime = microtime();

		$microTime = explode(' ',
				$microTime);

		$microTime = $microTime[1] + $microTime[0];

		$this->connectionTime = $microTime;
	}

	private function stopTime()
	{
		$microTime = microtime();

		$microTime = explode(' ',
				$microTime);

		$microTime = $microTime[1] + $microTime[0];

		$this->totalQueryCount++;

		$this->totalQueryTime += ($microTime - $this->connectionTime);
	}

	private function selectResultResource($query)
	{
		if(self::$Debug)
		{
			$this->checkQueryIfExecuted($query);

			$this->startTime();
		}

		if(!($resultResource = mysql_query($query,
				$this->connection())))
			throw new Mysql_exception($this,
					mysql_error($this->connection()));

		if(self::$Debug)
			$this->stopTime();

		return $resultResource;
	}

	public function SelectResultArrayArray($query)
	{
		$result = new MySQL_result($this->selectResultResource($query));

		return $result->FetchedDataArrayArray();
	}

	public function SelectResultArray($query)
	{
		$resultArrayArray = $this->SelectResultArrayArray($query);

		if(is_array($resultArrayArray))
			return current($resultArrayArray);
	}

	public function SelectResult($query)
	{
		$resultArray = $this->SelectResultArray($query);

		if(is_array($resultArray))
			return current($resultArray);
	}

	public function SelectAffectedRowCount($query)
	{
		$this->selectResultResource($query);

		$affectedRowCount = mysql_affected_rows($this->connection());

		if($affectedRowCount > 0)
			return $affectedRowCount;
	}

	public function SelectInput($input)
	{
		if(is_array($input))
			return $this->SelectArrayInput($input);

		if(is_bool($input))
			if($input)
				return '1';
			else
				return '2';

		if($input instanceof \DateTime)
			return "'" . $input->format('Y-m-d') . "'";

		if(is_int($input))
			return $input;

		if(is_float($input))
			return $input;

		if(!$input)
			return 'NULL';

		return "'" . mysql_real_escape_string(get_magic_quotes_gpc() ? stripslashes($input) : $input,
				$this->connection()) . "'";
	}

	public function SelectArrayInput(Array $inputArray)
	{
		$sQL = '(';

		$count = 0;

		foreach($inputArray AS $input)
		{
			if($count++)
				$sQL .= ', ';

			$sQL .= $this->SelectInput($input);
		}

		return $sQL . ')';
	}
}

class MySQL_exception extends \Exception
{
	private $mySQL;

	private $queryName;

	public function __Construct(MySQL $mySQL,
			$message,
			$queryName = NULL)
	{
		$this->mySQL = $mySQL;

		parent::__Construct($message);

		$this->queryName = $queryName;
	}

	public function __ToString()
	{
		$string = 'Database exception occured on "' . $this->mySQL . '"';

		if($this->queryName)
			$string .= " in '" . $this->queryName . "' query";

		$string .= ': ' . $this->message;

		return $string;
	}
}

class Mysql_result
{
	private $resultResource;

	public function __Construct($resultResource)
	{
		$this->resultResource = $resultResource;
	}

	private static function fetchedData($fieldType,
			$data)
	{
		if($data)
		{
			if($fieldType == 'int')
				return (int) $data;

			if($fieldType == 'real')
				return (float) $data;

			if($fieldType == 'date')
			{
				require_once __DIR__ . '/Date.php';

				return new Date($data);
			}

			return $data;
		}
	}

	private $fieldTypeArray;

	private function fieldType($fieldId)
	{
		if(!isset($this->fieldTypeArray[$fieldId]))
			$this->fieldTypeArray[$fieldId] = mysql_field_type($this->resultResource,
					$fieldId);

		return $this->fieldTypeArray[$fieldId];
	}

	private $fieldNameArray;

	private function fieldName($fieldId)
	{
		if(!isset($this->fieldNameArray[$fieldId]))
			$this->fieldNameArray[$fieldId] = mysql_field_name($this->resultResource,
					$fieldId);

		return $this->fieldNameArray[$fieldId];
	}

	private function fetchedDataArray(Array $dataArray)
	{
		$fetchedDataArray = array();

		foreach($dataArray AS $fieldId => $data)
			$fetchedDataArray[$this->fieldName($fieldId)] = self::fetchedData($this->fieldType($fieldId),
					$data);

		return $fetchedDataArray;
	}

	public function FetchedDataArrayArray()
	{
		$dataArrayArray = array();

		while($dataArray = mysql_fetch_row($this->resultResource))
			$dataArrayArray[] = $this->fetchedDataArray($dataArray);

		return $dataArrayArray;
	}
}
